home *** CD-ROM | disk | FTP | other *** search
/ PCGUIA 127 / PC Guia 127.iso / Software / Utils / FlashGot / Bin / flashgot-0.5.9.993-fx+fl+mz+ns+tb.xpi / components / flashgotService.js < prev   
Text File  |  2006-02-14  |  73KB  |  2,366 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2006 Giorgio Maone - g.maone@informaction.com
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. // *****************************************************************************
  23. // START DMS CLASSES
  24. // *****************************************************************************
  25.  
  26. const ASK_NEVER = [false, false, false];
  27.  
  28. // *** Base/Windows DMS ********************************************************
  29. function FlashGotDM(name) {
  30.   if(arguments.length>0) {
  31.     this._init(name);
  32.   }
  33. }
  34.  
  35. FlashGotDM.init=function(service) {
  36.   FlashGotDM.cleanup();
  37.   FlashGotDM.dms=[];
  38.   FlashGotDM.dmtests={};
  39.   FlashGotDM.executables={};
  40.   FlashGotDM.initDMS(service);
  41. };
  42.  
  43. FlashGotDM.cleanup=function() {
  44.   if(! ("executables" in FlashGotDM) ) return;
  45.   var name;
  46.   for(name in FlashGotDM.executables) {
  47.     var f=FlashGotDM.executables[name];
  48.     if(f instanceof Components.interfaces.nsIFile) {
  49.       try { f.remove(true); } catch(ex) {}
  50.     }
  51.   }
  52. };
  53.  
  54. FlashGotDM.prototype = {
  55.   _init: function(name) {
  56.     this.name=name;
  57.     const dms=FlashGotDM.dms;
  58.     var pos=dms.length;
  59.     if(name in dms) {
  60.       var other=dms[name];
  61.       for(var j=pos; j-->0;) {
  62.         if(dms[j]==other) {
  63.           pos=j;
  64.           break;
  65.         }
  66.       }
  67.     }
  68.     dms[name]=dms[pos]=this;
  69.   }
  70. ,
  71.   _service: null,
  72.   _cookieManager: null,
  73.   _exeFile: false,
  74.   _supported: null,
  75.   custom: false,
  76.   disabledLink: false,
  77.   disabledSel: false,
  78.   disabledAll: false,
  79.   exeName: "FlashGot.exe",
  80.   askPath: ASK_NEVER,
  81.   cookieSupport: true,
  82.   postSupport: false
  83. ,  
  84.   get codeName() {
  85.     return this.name.replace(/\W/g,"_");
  86.   }
  87.  
  88. ,
  89.   get service() {
  90.     return this._service?this._service:this._service=
  91.     Components.classes[SERVICE_CTRID].getService(Components.interfaces.nsISupports).wrappedJSObject;
  92.   }
  93. ,
  94.   get cookieManager() {
  95.     return this._cookieManager?this._cookieManager:this._cookieManager=
  96.       Components.classes["@mozilla.org/cookiemanager;1"
  97.         ].getService(Components.interfaces.nsICookieManager);
  98.   }
  99. ,
  100.   get exeFile() {
  101.     if(typeof(this._exeFile)=="object") return this._exeFile;
  102.     const exeName=this.exeName;
  103.     if(!exeName) return this._exeFile=null;
  104.     if(typeof(FlashGotDM.executables[exeName])=="object") {
  105.       return this._exeFile=FlashGotDM.executables[exeName];
  106.     }
  107.     try {
  108.       const exeFile=Components.classes["@mozilla.org/file/local;1"].createInstance(
  109.         Components.interfaces.nsILocalFile);
  110.       exeFile.initWithPath(this.service.globals.profDir.path);
  111.       exeFile.append(exeName);
  112.       if(exeFile.exists()) {
  113.         try { exeFile.remove(true); } catch(ex) { this.log(ex.message); }
  114.       }
  115.       this._exeFile=this.checkExePlatform(exeFile);
  116.       if(this._exeFile!=null && this.createExecutable()) {
  117.         this.log(this._exeFile.path+" created");
  118.       }
  119.     } catch(ex) {
  120.       this._exeFile=null;
  121.       this.log("Can't init "+exeName+":\n"+ex.message);
  122.     }
  123.     return FlashGotDM.executables[exeName]=this._exeFile;
  124.   }
  125. ,
  126.   checkExePlatform: function(exeFile) {
  127.     return /(\/.*\.exe)|(\\.*\.sh)$/i.test(exeFile.path)?null:exeFile;
  128.   }
  129. ,
  130.   get supported() {
  131.     if(typeof(this._supported)=="boolean") return this._supported;
  132.     if(this.customSupportCheck && this.customSupportCheck()) return true;
  133.     if(!this.exeName) return true;
  134.     if(!this.exeFile) return false;
  135.     
  136.     var dmtest;
  137.     if(typeof(FlashGotDM.dmtests[this.exeName])!="string") {
  138.       const dmtestFile=this.service.tmpDir.clone();
  139.       dmtestFile.append(this.exeName+".test");
  140.       try {
  141.         this.launchSupportTest(dmtestFile);
  142.         this.log(dmtest=this.service.readFile(dmtestFile)); 
  143.       } catch(ex) {
  144.         this.log(ex.message);
  145.         dmtest="";
  146.       }
  147.       FlashGotDM.dmtests[this.exeName]=dmtest;
  148.     } else dmtest=FlashGotDM.dmtests[this.exeName];
  149.     return this._supported=dmtest.indexOf(this.name+"|OK")>-1;
  150.   }
  151. ,
  152.   launchSupportTest: function (testFile) {
  153.     this.runNative(["-o",testFile.path],true);
  154.   },
  155.   
  156.   shouldList: function() {
  157.     return this.supported;
  158.   }
  159. ,
  160.   log: function(msg) {
  161.     this.service.log(msg);
  162.   }
  163. ,
  164.   updateProgress: function(links, idx , len) {
  165.    
  166.     if((idx % 100) == 0) {
  167.       if(!len) {
  168.         links.progress.update(100);
  169.         return;
  170.       }
  171.       links.progress.update(50 + 49 * idx / len);
  172.     }
  173.     
  174.   }
  175. ,
  176.   isValidLink: null
  177. ,
  178.   createJobHeader: function(links, opType) {
  179.     return links.length+";"+this.name+";"+
  180.       (this.service.getPref(this.codeName+".quiet."+opType,false)
  181.         ?this.service.OP_QET:opType)
  182.       +";"+links.folder+";\n"
  183.   }
  184. ,
  185.   createJobBody: function(links) {
  186.     var job="";
  187.     var l,url;
  188.     const len=links.length;
  189.     this.checkCookieSupport();
  190.     var postData = links.postData || "";
  191.  
  192.     for(var j=0; j<len; j++) {
  193.       job+="\n"+(url=(l=links[j]).href) + "\n" +
  194.            l.description + "\n" +
  195.            this.getCookie(l,links) + "\n"
  196.            + postData;
  197.       this.updateProgress(links,j,len);
  198.     }
  199.     return job;
  200.   }
  201. ,
  202.   createJob: function(links,opType) {
  203.     var job=this.createJobHeader(links,opType) 
  204.     + this.getReferrer(links)
  205.     + this.createJobBody(links)+"\n";
  206.      if(job.substring(job.length-1)!="\n") {
  207.       job+="\n";
  208.      }
  209.     if(typeof(links.document)=="object") {
  210.       job+= links.document.referrer+ "\n" +links.document.cookie;
  211.     } else {
  212.       job+="\n";
  213.     }
  214.     return job;
  215.   }
  216. ,
  217.   _bgJob: true,
  218.   get bgJob() {
  219.     return this._bgJob && this.service.bgProcessing
  220.       ; 
  221.   }
  222. ,
  223.   download: function(links, opType) {
  224.     try {
  225.       links.folder=(links.length>0)?this.selectFolder(links, opType):""; 
  226.       this.performJob(this.createJob(links,opType));
  227.     } catch(ex) {
  228.       this.log(ex.message);
  229.     } finally {
  230.       this.updateProgress(links, 0); // 100%
  231.     }
  232.   }
  233. ,
  234.   getReferrer: function(links) {
  235.     return this.service.getPref("autoReferrer",true) ?
  236.       (links.referrer || 
  237.         typeof(links.document)=="object" && links.document.URL ||
  238.         links[0] && links[0].href || 
  239.         "about:blank"
  240.       ) : this.service.getPref("fakeReferrer","");
  241.   }
  242. ,
  243.   checkCookieSupport: function() {
  244.     this.getCookie=this.cookieSupport && !this.service.getPref("omitCookies")
  245.     ?this._getCookie
  246.     :function() { return ""; }
  247.     ;
  248.   }
  249. ,
  250.   getCookie: function() { return ""; }
  251. ,
  252.   _getCookie: function(link,links) {
  253.     if(!this.cookieSupport) return (this.getCookie=function() { return ""; })();
  254.     
  255.     var host,cookies;
  256.     if(cookies=links.cookies) {
  257.       host=link.host;
  258.       if(host) {
  259.         var c=cookies[host];
  260.         return c?c:"";
  261.       }
  262.       return "";
  263.     }
  264.     
  265.     var j,objCookie;
  266.     const hostCookies={};
  267.     
  268.     var l,parts;
  269.     for(j=links.length; j-->0;) {
  270.       l=links[j];
  271.       parts=l.href.match(/http[s]{0,1}:\/\/([^\/]+\.[^\/]+)/i); // host?
  272.       if(parts) {
  273.         hostCookies[l.host=parts[1]]="";
  274.       } else {
  275.         l.host=null;
  276.       }
  277.     }
  278.     
  279.     var cookieHost,cookieTable,tmpCookie;
  280.     const domainCookies={};
  281.  
  282.     for(var iter = this.cookieManager.enumerator; iter.hasMoreElements();) {
  283.       if((objCookie=iter.getNext()) instanceof Components.interfaces.nsICookie) {
  284.         cookieHost=objCookie.host;
  285.         if(cookieHost.charAt(0)==".") {
  286.           cookieHost=cookieHost.substring(1);
  287.           cookieTable=domainCookies;
  288.           if(typeof(tmpCookie=domainCookies[cookieHost])!="string") {
  289.             tmpCookie="";
  290.           }
  291.         } else {
  292.           if(typeof(tmpCookie=hostCookies[cookieHost])!="string") continue;
  293.           cookieTable=hostCookies;
  294.         }
  295.         cookieTable[cookieHost]=tmpCookie.concat(objCookie.name+"="+objCookie.value+"; ");
  296.       }
  297.     }
  298.     
  299.    
  300.     for(cookieHost in hostCookies) {
  301.       var dotPos;
  302.       for(host=cookieHost; (dotPos=host.indexOf('.'))>=0; ) { 
  303.         if(tmpCookie=domainCookies[host]) {
  304.           hostCookies[cookieHost]+=tmpCookie;
  305.         }
  306.         host=host.substring(dotPos+1);
  307.       }
  308.     }
  309.     
  310.     links.cookies=hostCookies;
  311.     return this.getCookie(link,links);
  312.   }
  313. ,
  314.   createJobFile: function(job) {
  315.     const jobFile=this.service.tmpDir.clone();
  316.     jobFile.append("flashgot.fgt");
  317.     jobFile.createUnique(0,0700);
  318.     this.service.writeFile(jobFile, job);
  319.     return jobFile;
  320.   }
  321.   _waitForNative: true,
  322.   get waitForNative() {
  323.     return this._waitForNative && this.service.bgProcessing;
  324.   }
  325. ,
  326.   performJob: function(job) {
  327.     const jobFile=this.createJobFile(job);
  328.     this.runNative([jobFile.path],this.waitForNative);
  329.   }
  330. ,
  331.   createExecutable: function() {
  332.     const exeFile=this.exeFile;
  333.     if(!exeFile) return false;
  334.     
  335.     const cc=Components.classes;
  336.     const ci=Components.interfaces;
  337.     const ios=cc['@mozilla.org/network/io-service;1'].getService(ci.nsIIOService);
  338.     const bis=cc['@mozilla.org/binaryinputstream;1'].createInstance(ci.nsIBinaryInputStream);
  339.     
  340.     var channel;
  341.     bis.setInputStream((
  342.       channel=
  343.         ios.newChannel("chrome://flashgot/content/"+this.exeName,null,null)
  344.     ).open())
  345.     ;
  346.     const bytesCount=channel.contentLength;
  347.     
  348.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  349.       ci.nsIFileOutputStream);
  350.     
  351.     try {
  352.       
  353.       os.init(exeFile,0x02 | 0x08, 0700, 0);
  354.       const bos=cc['@mozilla.org/binaryoutputstream;1'].createInstance(ci.nsIBinaryOutputStream);
  355.       bos.setOutputStream(os);
  356.       bos.writeByteArray(bis.readByteArray(bytesCount),bytesCount);
  357.       bos.close();
  358.  
  359.     } catch(ioErr) { // locked?
  360.       try {
  361.         if(exeFile.exists()) { // security check: it must be the right exe!
  362.           const testBis=cc['@mozilla.org/binaryinputstream;1'].createInstance(
  363.             ci.nsIBinaryInputStream);
  364.           testBis.setInputStream(
  365.             (channel=ios.newChannelFromURI(ios.newFileURI(exeFile))).open());
  366.           const error=new Error("Old, corrupt or unlegitemately modified "
  367.             +exeFile.path
  368.             +".\nThe file is locked: please delete it manually\n");
  369.             +ioErr.message;
  370.           if(channel.contentLength!=bytesCount) throw error;
  371.          
  372.           const legitimateData=bis.readByteArray(bytesCount);
  373.           const testData=testBis.readByteArray(bytesCount);
  374.           for(var j=bytesCount; j-->0;) {
  375.             if(legitimateData[j]!=testData[j]) throw new error;
  376.           }
  377.         } else throw ioErr;
  378.       } catch(unrecoverableErr) {
  379.          this.log("Error creating native executable\n"+exeFile.path+"\n"+unrecoverableErr.message);
  380.       }
  381.     } finally {
  382.       os.close();
  383.       bis.close();
  384.     }
  385.     
  386.     return true;
  387.   }
  388. ,
  389.   runNative: function(args,blocking,exeFile) {
  390.     try {
  391.       if(typeof(exeFile)=="object"
  392.         || (exeFile=this.exeFile).exists()
  393.         || this.createExecutable()) {
  394.         const proc=Components.classes['@mozilla.org/process/util;1'].createInstance(
  395.           Components.interfaces.nsIProcess);
  396.         proc.init(exeFile);
  397.         this.log("Running "+exeFile.path+" ("+(blocking?"blocking":"async")+")");
  398.         proc.run(blocking,args,args.length,{});
  399.         if(blocking && proc.exitValue!=0) {
  400.           this.log("Warning: native invocation of\n"
  401.             +exeFile.path
  402.             +"\nwith arguments <"
  403.             +args.join(" ")
  404.             +">\nreturned "+proc.exitValue);
  405.         }
  406.         return proc.exitValue;
  407.       } else {
  408.         this.log("Bad executable "+exeFile);
  409.       }
  410.     } catch(err) {
  411.       this.log("Error running native executable:\n"+exeFile.path+" "+args.join(" ")+"\n"+err.message);
  412.     }  
  413.     return 0xffffffff;
  414.   }
  415. ,
  416.   getWindow: function() {
  417.     return this.service.getWindow();
  418.   }
  419. ,
  420.   selectFolder: function(links,opType) { 
  421.     const cc=Components.classes;
  422.     const ci=Components.interfaces;
  423.    
  424.     const autoPref_FF="browser.download.useDownloadDir";
  425.     const autoPref_Moz="browser.download.autoDownload";
  426.     
  427.     var initialDir=null;
  428.     var downloadDir=null;
  429.     links.quickDownload = false;
  430.     
  431.     const pref = cc["@mozilla.org/preferences-service;1"].getService(ci.nsIPrefBranch);
  432.     
  433.     function findDownloadDir(prefName) {
  434.       try {
  435.         downloadDir = initialDir = pref.getComplexValue("browser.download.dir", ci.nsILocalFile);
  436.         return prefName;
  437.       } catch(ex) {
  438.         return "";
  439.       }
  440.     }
  441.   
  442.     const downloadDirPref = findDownloadDir("browser.download.dir") ||
  443.                     findDownloadDir("browser.download.downloadDir") || 
  444.                     findDownloadDir("browser.download.defaultFolder") ||
  445.                     "browser.download.dir"; 
  446.    
  447.     try {
  448.       links.quickDownload = pref.getBoolPref(autoPref_FF);
  449.     } catch(noFFEx) {
  450.       try {
  451.         links.quickDownload=pref.getBoolPref(autoPref_Moz);
  452.       } catch(noMozEx) {}
  453.     }
  454.    
  455.     if(!this.askPath[opType]) return "";
  456.     
  457.     if(downloadDir && downloadDir.exists() && downloadDir.isDirectory()  && 
  458.         links.quickDownload) {
  459.       return downloadDir.path;
  460.     }
  461.     
  462.     var title;
  463.     try {
  464.       var bundle = cc["@mozilla.org/intl/stringbundle;1"].getService(ci.nsIStringBundleService);
  465.       bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  466.       title = bundle.GetStringFromName("myDownloads");
  467.     } catch(ex) {
  468.       title="Download directory";
  469.     }
  470.     title='FlashGot ('+this.name+') - '+title;
  471.     
  472.     const fp = cc["@mozilla.org/filepicker;1"].createInstance(ci.nsIFilePicker);
  473.     const win=this.getWindow();
  474.     fp.init(win, title, ci.nsIFilePicker.modeGetFolder);
  475.     try {
  476.       if (initialDir &&  initialDir.exists() && initialDir.isDirectory()) {
  477.         fp.displayDirectory = initialDir;
  478.       }
  479.     } catch (ex) { this.log(ex); }
  480.     
  481.     fp.appendFilters(ci.nsIFilePicker.filterAll);
  482.  
  483.     if (fp.show()==ci.nsIFilePicker.returnOK) {
  484.       var localFile = fp.file.QueryInterface(ci.nsILocalFile);
  485.       pref.setComplexValue(downloadDirPref, ci.nsILocalFile, localFile);
  486.       var path=new String(localFile.path);
  487.       path._fgSelected=true;
  488.       return path;
  489.     }
  490.     
  491.     throw new Error("Download cancelled by user");
  492.   }
  493.   
  494. }
  495.  
  496.  
  497.  
  498.  
  499. // *** Unix-like DMS ***********************************************************
  500. function FlashGotDMX(name,cmd,argsTemplate) {
  501.   if(arguments.length!=0) {
  502.     this._init(name);
  503.     const cmds=FlashGotDMX.prototype.unixCmds;
  504.     cmds[cmds.length] = {longName: name, shortName: cmd};
  505.     this.unixCmd = cmd;
  506.     if(argsTemplate) this.argsTemplate = argsTemplate;
  507.   }
  508. }
  509. FlashGotDMX.prototype=new FlashGotDM();
  510. FlashGotDMX.constructor=FlashGotDMX;
  511. FlashGotDMX.prototype.exeName="flashgot.sh";
  512. FlashGotDMX.prototype.cookieSupport=false;
  513. FlashGotDMX.prototype.askPath=[true,true,true];
  514. FlashGotDMX.prototype.unixCmds=[];
  515. FlashGotDMX.prototype.unixShell=null;
  516. FlashGotDMX.prototype.argsTemplate="[URL]";
  517. FlashGotDMX.prototype.launchSupportTest=function(testFile) {
  518.   const cmds=this.unixCmds;
  519.   var script="(\n";
  520.   var cmd;
  521.   for(var j=cmds.length; j-->0;) {
  522.     cmd=cmds[j];
  523.     script+=" [ -x \"`which '"+cmd.shortName+"'`\" ] && echo '"
  524.       +cmd.longName+"|OK' || echo '"+cmd.longName+"|KO'\n"; 
  525.   }
  526.   script+=") > '"+ testFile.path + "'\n"; 
  527.   this.performJob(script,true);
  528. };
  529.  
  530. FlashGotDMX.prototype.createCmdLine=function(URL,REFERER,COOKIE,FOLDER,POST) {
  531.   const parms={ URL: URL, REFERER: REFERER, COOKIE: COOKIE, FOLDER: FOLDER, POST: POST };
  532.   return this.unixCmd+ " " +
  533.     this.argsTemplate.replace(/\[(.*?)(URL|REFERER|COOKIE|FOLDER|POST)(.*?)\]/g,
  534.       function(all,before,parm,after) { 
  535.           v=parms[parm]; 
  536.           return typeof(v)!="undefined" && v!=null
  537.             ?before+v+after
  538.             :"";
  539.       }
  540.       ) +" &\n";
  541. };
  542. FlashGotDMX.prototype.shellEsc = function(s) {
  543.   return s?s.replace(/([\\\*\?\[\]\$&<>\|\(\)\{\};"'`])/g,"\\$1").replace(/\s/g,"\\ "):null;
  544. };
  545. FlashGotDMX.prototype.createJob=function(links,opType) {
  546.   const shellEsc = this.shellEsc;
  547.   // basic implementation
  548.   const len=links.length;
  549.   const folder=shellEsc(links.folder);
  550.   const referrer=shellEsc(this.getReferrer(links));
  551.   const postData=shellEsc(links.postData);
  552.   var job="";
  553.   var l,url;
  554.   this.checkCookieSupport();
  555.   for(var j=0; j<len; j++) {
  556.     l=links[j];
  557.     url=l.href;
  558.     job+=this.createCmdLine(
  559.       shellEsc(url), 
  560.       referrer, 
  561.       shellEsc(this.getCookie(l,links)), 
  562.       folder, 
  563.       postData);
  564.     this.updateProgress(links, j, len);
  565.   }
  566.   return job;
  567. };
  568. FlashGotDMX.prototype.performJob=function(job,blocking) {
  569.   const jobFile=this.createJobFile("#!"+this.unixShell.path+"\n"+job);
  570.   jobFile.permissions=0700;
  571.   this.runNative([],
  572.     this.waitForNative || (typeof(blocking)!="undefined" && blocking),
  573.     jobFile);
  574. };
  575. FlashGotDMX.prototype.checkExePlatform=function(exeFile) {
  576.   const f=Components.classes["@mozilla.org/file/local;1"].createInstance(
  577.     Components.interfaces.nsILocalFile);
  578.   try {
  579.     f.initWithPath("/bin/sh");
  580.     if(f.exists()) {
  581.       FlashGotDMX.prototype.unixShell=f;
  582.       return exeFile;
  583.     }
  584.     this.log(f.path+" not found");
  585.   } catch(ex) {
  586.     this.log(ex.message);
  587.   }
  588.   return null;
  589. };
  590. FlashGotDMX.prototype.createExecutable=function() {
  591.   return false;
  592. };
  593.  
  594.  
  595.  
  596. // *** Mac OS X DMS ************************************************************
  597. function FlashGotDMMac(name, creatorId, macAppName) {
  598.   if(arguments.length!=0) {
  599.     this._initMac(name,creatorId,macAppName);
  600.   }
  601. }
  602. FlashGotDMMac.prototype=new FlashGotDM();
  603. FlashGotDMMac.constructor=FlashGotDMMac;
  604. FlashGotDMMac.prototype.exeName="flashgot-mac.sh";
  605. FlashGotDMMac.prototype.cookieSupport=false;
  606. FlashGotDMMac.prototype.OSASCRIPT="/usr/bin/osascript";
  607. FlashGotDMMac.prototype.macCreators=[];
  608. FlashGotDMMac.prototype._initMac=function(name, creatorId, macAppName) {
  609.   this._init(name);
  610.   if(creatorId) {
  611.     const creators=FlashGotDMMac.prototype.macCreators;
  612.     creators[creators.length] = {name: name, id: creatorId};
  613.   }
  614.   this.macAppName = macAppName?macAppName:name;
  615. };
  616. FlashGotDMMac.prototype.createScriptLauncher=function(scriptPath) {
  617.   return "#!/bin/sh\n"
  618.     +this.OSASCRIPT+" '"+scriptPath+"'";
  619. };
  620. FlashGotDMMac.prototype.checkExePlatform=function(exeFile) {
  621.   const f=Components.classes["@mozilla.org/file/local;1"].createInstance(
  622.     Components.interfaces.nsILocalFile);
  623.   try {
  624.     f.initWithPath(this.OSASCRIPT);
  625.     if(f.exists()) return exeFile;
  626.     this.log(f.path+" not found");
  627.   } catch(ex) {
  628.     this.log(ex.message);
  629.   }
  630.   return null;
  631. };
  632. FlashGotDMMac.prototype.createExecutable=function() {
  633.   const exeFile=this.exeFile;
  634.   if(exeFile) {
  635.     try {
  636.      const script=this.service.tmpDir.clone();
  637.      script.append("flashgot-test.scpt");
  638.      FlashGotDMMac.prototype.testAppleScript=script;
  639.      script.createUnique(0,0700);
  640.      if(exeFile.exists()) exeFile.remove(true);
  641.      exeFile.create(0,0700);
  642.      this.service.writeFile(exeFile, this.createScriptLauncher(script.path));
  643.      exeFile.permissions=0700;
  644.      return true;
  645.     } catch(ex) {
  646.       this.log(ex.message);
  647.     }
  648.   }
  649.   return false;
  650. };
  651. FlashGotDMMac.prototype.launchSupportTest=function(testFile) {
  652.   const creators=FlashGotDMMac.prototype.macCreators;
  653.   const RESP="    do shell script \"echo >>'"+testFile.path+"' '\" & theName & \"|";
  654.   function response(msg) {
  655.     return RESP+msg+"'\"\n";
  656.   }
  657.   var s="on test(theName, theCreator)\n"
  658.        +" tell app \"Finder\"\n"
  659.        +"  set theResponse to \"KO\"\n"
  660.        +"  try\n"
  661.        +"   get name of application file id theCreator\n"
  662.        +"   if result contains theName then\n"
  663.        +"     set theResponse to \"OK\"\n"
  664.        +"   end if\n"
  665.        +"  on error\n"
  666.        +"  end try\n"
  667.        +"  do shell script \"echo >>'"+testFile.path+"' '\" & theName & \"|\" & theResponse & \"'\"\n"
  668.        +" end tell\n"
  669.        +"end test\n"
  670.        +"\n";
  671.   for(var j=creators.length; j-->0;) {
  672.      s+='get test("'+creators[j].name+'","'+creators[j].id+'")\n'; 
  673.    }
  674.    this.service.writeFile(this.testAppleScript,s);
  675.    this.runNative([],true,this.exeFile);
  676. };
  677. FlashGotDMMac.prototype.performJob=function(job) {
  678.   const script=this.createJobFile(job);
  679.   const launcher=this.createJobFile(this.createScriptLauncher(script.path));
  680.   launcher.permissions=0700;
  681.   this.runNative([],this.waitForNative,launcher);
  682. };
  683. FlashGotDMMac.prototype.createJob=function(links,opType) {
  684.   const referrer=this.getReferrer(links);
  685.   var job = "tell application \""+ this.macAppName+ "\"\n";
  686.   for(var j=0,len=links.length; j<len; j++) {
  687.     job+="GetURL \""+links[j].href+"\" from \""+ referrer  +"\"\n";
  688.     this.updateProgress(links, j, len);
  689.   }
  690.   job+="end tell\n";
  691.   return job;
  692. };
  693.  
  694.  
  695.  
  696. // *** Custom DMS **************************************************************
  697. function FlashGotDMCust(name) {
  698.   if(arguments.length==0 || (!name) || (!name.length)) return;
  699.   name=name.replace(/,/g," ");
  700.   this._init(name);
  701.   
  702.   const cc=Components.classes;
  703.   const ci=Components.interfaces;
  704.   const svc=this.service;
  705.   this.prefsBase="custom."+this.codeName+".";
  706. }
  707.  
  708. FlashGotDMCust.init=function(service) {
  709.   const names=service.getPref("custom","").split(/\s*,\s*/);
  710.   for(var j=names.length; j-->0;) {
  711.     new FlashGotDMCust(names[j]);
  712.   }
  713. }
  714.  
  715. FlashGotDMCust.persist=function(service) {
  716.   const dms=FlashGotDM.dms;
  717.   const cdms=[];
  718.   for(var j=dms.length; j-->0;) {
  719.     if(dms[j].custom) cdms.push(dms[j].name);
  720.   }
  721.   service.setPref("custom", cdms.join(","));
  722. }
  723.  
  724. FlashGotDMCust.prototype=new FlashGotDM();
  725. delete FlashGotDMCust.prototype.launchSupportTest;
  726. delete FlashGotDMCust.prototype.exeFile;
  727.  
  728. FlashGotDMCust.constructor=FlashGotDMCust;
  729.  
  730. FlashGotDMCust.prototype.custom = true;
  731. FlashGotDMCust.prototype._supported = true;
  732. FlashGotDMCust.prototype.__defineGetter__("exeFile",function() {
  733.   try {
  734.     return this.service.prefs.getComplexValue(this.prefsBase+"exe", 
  735.       Components.interfaces.nsILocalFile);
  736.   } catch(ex) {
  737.     return null;
  738.   }
  739. });
  740. FlashGotDMCust.prototype.__defineSetter__("exeFile",function(v) {
  741.   try {
  742.     if(v) {
  743.       this.service.prefs.setComplexValue(this.prefsBase+"exe", 
  744.           Components.interfaces.nsILocalFile,v);
  745.       return v;
  746.     }
  747.   } catch(ex) {
  748.     return null;
  749.   }
  750. });
  751.  
  752. FlashGotDMCust.prototype.__defineGetter__("argsTemplate",function() {
  753.   var t=this.service.getPref(this.prefsBase+"args","[URL]");
  754.   return /['"`]/.test(t)?this.argsTemplate=t:t;  
  755. });
  756. FlashGotDMCust.prototype.__defineSetter__("argsTemplate",function(v) {
  757.   if(!v) {
  758.     v="";
  759.   } else {
  760.     v=v.replace(/['"`]/g,"");
  761.   }
  762.   this.service.setPref(this.prefsBase+"args",v);
  763.   return v;
  764. });
  765.  
  766. FlashGotDMCust.prototype.cookieSupport=false;
  767. FlashGotDMCust.prototype.askPath=[true,true,true];
  768.  
  769. FlashGotDMCust.prototype.download = function(links,opType) {
  770.   const t=this.argsTemplate;
  771.   this.cookieSupport=/\[.*?COOKIE.*?\]/.test(t);
  772.   this.askPath[opType]=/\[.*?FOLDER.*?\]/.test(t);
  773.   var exeFile=this.exeFile;
  774.   // portable hacks
  775.   if(exeFile && !exeFile.exists()) {
  776.     // try changing the first part of path
  777.     var path = exeFile.path;
  778.     var profPath = this.service.profDir.path;
  779.     var pos1, pos2;
  780.     if(path[1] == ":" && profPath[1] == ":") { 
  781.       // easy, it's Windows, swap drive letter
  782.       path = profPath[0] + path.substring(1);
  783.     } else if(path.indexOf("/mount/") == 0 && profPath.indexOf("/mount/") == 0) {
  784.       pos1 = path.indexOf("/", 7);
  785.       pos2 = profPath.indexOf("/", 7);
  786.       path = "/mount/" + profPath.substring(7, pos2) + path.substring(pos1); 
  787.     } else if((pos1 = path.indexOf("/",1)) > 0 && (pos2 = profPath.indexOf("/", 1)) > 0) {
  788.       path = profPath.substring(0, pos2) + path.substring(pos1);
  789.     } else exeFile = null;
  790.     if(exeFile) {
  791.       exeFile = exeFile.clone().QueryInterface(Components.interfaces.nsILocalFile).initWithPath(path);
  792.       if(!exeFile.exists()) exeFile = null;
  793.     }
  794.   }
  795.   links.exeFile= (exeFile || 
  796.     (exeFile=this.exeFile=this.locateExeFile())) ? exeFile : null;
  797.   FlashGotDM.prototype.download.call(this, links, opType);
  798. };
  799.  
  800. FlashGotDMCust.prototype.locateExeFile = function(name) {
  801.   const cc=Components.classes;
  802.   const ci=Components.interfaces;
  803.   if(!name) name=this.name;
  804.   var title=this.service.getString("custom.exeFile");
  805.   title='FlashGot ('+name+') - '+title;
  806.   
  807.   const fp = cc["@mozilla.org/filepicker;1"].createInstance(ci.nsIFilePicker);
  808.   const win=this.getWindow();
  809.   fp.init(win, title, ci.nsIFilePicker.modeOpen);
  810.   fp.appendFilters(ci.nsIFilePicker.filterApps);
  811.   fp.appendFilters(ci.nsIFilePicker.filterAll);
  812.  
  813.   if (fp.show() == ci.nsIFilePicker.returnOK) {
  814.     var file = fp.file.QueryInterface(ci.nsILocalFile);
  815.     if(file.exists()) {
  816.       return file;
  817.     }
  818.   }
  819.   return null;
  820. };
  821. FlashGotDMCust.prototype.PLACEHOLDERS=["URL","REFERER","COOKIE","FOLDER","POST", "UFILE", "CFILE"];
  822. FlashGotDMCust.prototype.postSupport = true;
  823. FlashGotDMCust.prototype._addParts=function(a,s) {
  824.   var parts=s.split(/\s+/);
  825.   var k,p;
  826.   for(k in parts) {
  827.     if((p=parts[k]).length) {
  828.       a[a.length] = p;
  829.     }
  830.   } 
  831. };
  832. FlashGotDMCust.prototype.makeArgs=function(URL, REFERER, COOKIE, FOLDER, POST,  CFILE, UFILE) {
  833.   const parms={ URL: URL, REFERER: REFERER, COOKIE: COOKIE, FOLDER: FOLDER, POST: POST,  CFILE: CFILE, UFILE: UFILE};
  834.   const args=[];
  835.   var t=this.argsTemplate;
  836.   var j,v,len,s;
  837.  
  838.   var idx;
  839.   for(var m; 
  840.       m=t.match( /([\n.]*?)\[([\n.]*?)(\S*)(URL|REFERER|COOKIE|FOLDER|POST|CFILE|UFILE)([^\s\]]*)([\n.]*?)\]([\n.]*?)/); 
  841.       t=t.substring(idx+m[0].length) ) {
  842.     
  843.     if((idx=m.index)>0) {
  844.       this._addParts(args,t.substring(0,idx));
  845.     }
  846.     
  847.     v=parms[m[4]];
  848.     len=typeof(v) != "undefined" && v!=null?m.length:2;
  849.     for(j =1 ; j < len; j++) {
  850.       if(j == 3) {
  851.         args[args.length] = m[3] + v + m[5];
  852.         j = 5;
  853.       } else {
  854.         s=m[j];
  855.         if(s.length) {
  856.           this._addParts(args, s);
  857.         }
  858.       }
  859.     }
  860.   }
  861.   
  862.   if(t.length) {
  863.     this._addParts(args,t);
  864.   }
  865.   return args;
  866. };
  867.  
  868. FlashGotDMCust.prototype.createJob=function(links,opType) {
  869.   return { links: links, opType: opType };
  870. };
  871.  
  872. FlashGotDMCust.prototype.performJob=function(job) {
  873.   const links=job.links;
  874.   const exeFile=links.exeFile;
  875.   if(!exeFile) return;
  876.   
  877.   const len=links.length;
  878.   if(len < 1) return;
  879.   
  880.   const folder=links.folder;
  881.   const referrer = this.getReferrer(links);
  882.   const postData = links.postData;
  883.   var l, url;
  884.   
  885.   this.checkCookieSupport();
  886.   var cookieFile;
  887.   if(this.service.getPref("omitCookies")) {
  888.     cookieFile = null;
  889.   } else {
  890.     cookieFile = this.service.profDir.clone();
  891.     cookieFile.append("cookies.txt");
  892.     cookieFile = cookieFile.path;
  893.   }
  894.   
  895.   var j;
  896.   if(/\[[^\]]*UFILE[^\]]*\]/.test(this.argsTemplate)) {
  897.     // we must create a file list
  898.     var urlList = "";
  899.     for(j = 0; j < len; j++) {
  900.       urlList += links[j].href + "\n";
  901.     }
  902.     var args;
  903.     this.runNative(args = this.makeArgs(
  904.       null,
  905.       referrer,
  906.       null,
  907.       folder,
  908.       postData,
  909.       cookieFile,
  910.       this.createJobFile(urlList).path
  911.       ),
  912.     false, exeFile);
  913.     this.log("wget " + args.join(" "));
  914.   } else {
  915.    
  916.     for(j = 0; j < len; j++) {
  917.       l=links[j];
  918.       url=l.href;
  919.       this.runNative(  
  920.         this.makeArgs(
  921.           url, 
  922.           referrer, 
  923.           this.getCookie(l, links), 
  924.           folder, 
  925.           postData,
  926.           cookieFile,
  927.           null  // URL LIST FILE
  928.           ),
  929.       false, exeFile);
  930.       this.updateProgress(links, j, len);
  931.     }
  932.   }
  933. };
  934. FlashGotDMCust.prototype.checkExePlatform=function(exeFile) {
  935.   return exeFile;
  936. };
  937. FlashGotDMCust.prototype.createExecutable=function() {
  938.   return false;
  939. };
  940.  
  941.  
  942. // *****************************************************************************
  943. // END DMS CLASSES
  944. // *****************************************************************************
  945.  
  946. // DMS initialization
  947.  
  948. FlashGotDM.initDMS=function(service) {
  949.   var dm;
  950.   
  951.   dm=new FlashGotDM("Download Accelerator Plus");
  952.   dm.exclusive=true;
  953.   
  954.   new FlashGotDM("Download Master");
  955.   
  956.   new FlashGotDM("FlashGet");
  957.   
  958.   dm=new FlashGotDM("Free Download Manager");
  959.   dm._waitForNative=false;
  960.   
  961.   new FlashGotDM("FreshDownload");
  962.   
  963.   dm=new FlashGotDM("GetRight");
  964.   dm.super_download=FlashGotDM.prototype.download;
  965.   dm.super_createJob=FlashGotDM.prototype.createJob;
  966.   dm.download=function(links, opType) {
  967.     const service=this.service;
  968.     if(opType==service.OP_ONE && !service.getPref("GetRight.quick")) {
  969.       opType=service.OP_SEL;
  970.     }
  971.     this.super_download(links,opType);
  972.   };
  973.   dm.createJob=function(links, opType) {
  974.     const service=this.service;
  975.     var folder=links.folder;
  976.     if(!(folder && folder._fgSelected)) folder=false;
  977.     
  978.     var referrer=this.getReferrer(links);
  979.     
  980.     switch(opType) {
  981.       case service.OP_ONE:
  982.         var job=this.super_createJob(links,opType);
  983.         if(this.service.getPref("GetRight.old")) job+="\nold";
  984.         return job;
  985.       case service.OP_SEL:
  986.       case service.OP_ALL:
  987.         var urlList="";
  988.         var referrerLine=(referrer && referrer.length>0)?"\r\nReferer: "+referrer+"\r\n":"\r\n";
  989.         var l,decodedURL, urlParts, fileSpec, cookie;
  990.         for(var j=0, len=links.length; j<len; j++) {
  991.           l=links[j];
  992.           
  993.           if(folder) {
  994.             decodedURL=l.href;
  995.             try { decodedURL=decodeURI(decodedURL) } catch(ex) {};
  996.             urlParts=decodedURL.match(/\/\/.+[=\/]([^\/]+\.\w+)/);
  997.             if(!urlParts) urlParts=l.href.match(/.*\/(.*\w+.*)/);
  998.             if(urlParts && (fileSpec=urlParts[1])
  999.               // && (links.length==1 ||  !/\.(php|[\w]?htm[l]?|asp|jsp|do|xml|rdf|\d+)$/i.test(fileSpec))
  1000.              ) {  
  1001.               urlParts=fileSpec.match(/(.*\.\w+).*/);
  1002.               if(urlParts) fileSpec=urlParts[1];
  1003.               fileSpec="File: "+folder+"\\"+fileSpec.replace(/[^\w\.-]/g,'_')+"\r\n";
  1004.             } else continue;
  1005.           } else fileSpec="";
  1006.           urlList+="URL: "+l.href
  1007.             +"\r\nDesc: "+l.description
  1008.             +referrerLine+fileSpec;
  1009.           if(cookie=this.getCookie(l,links)) {
  1010.             urlList+="Cookie: " + cookie + "\r\n";
  1011.           }
  1012.           this.updateProgress(links, j, len);
  1013.         }
  1014.         var file=service.tmpDir.clone();
  1015.         file.append("flashgot.grx");
  1016.         file.createUnique(0,0600);
  1017.         var charset=null;
  1018.         try {
  1019.           charset=service.getPref("GetRight.charset",
  1020.             service.prefService.QueryInterface(Components.interfaces.nsIPrefBranch
  1021.             ).getComplexValue("intl.charset.default",
  1022.               Components.interfaces.nsIPrefLocalizedString).data);
  1023.         } catch(ex) {}
  1024.         service.writeFile(file, urlList, charset);
  1025.         referrer=file.path;
  1026.         break;
  1027.     }
  1028.     var cmdOpts="/Q";
  1029.     if(service.getPref("GetRight.autostart",false)) { // CHECK ME!!!
  1030.       cmdOpts+="\n /AUTO";
  1031.     }
  1032.     return this.createJobHeader({ length: 0, folder: "" },opType) +
  1033.       referrer + "\n" + cmdOpts;
  1034.   };
  1035.   dm.askPath=[false,true,true];
  1036.   
  1037.   new FlashGotDM("HiDownload");
  1038.   new FlashGotDM("InstantGet");
  1039.   
  1040.   dm = new FlashGotDM("iGetter Win");
  1041.   dm.__defineGetter__("supported", 
  1042.     function() { return  "nsIGetterMoz" in Components.interfaces; });
  1043.   dm.createJob = function(links, opType) {
  1044.     var job = this.getReferrer(links) + "\r\n";
  1045.     for(var j=0; j < links.length; j++) {
  1046.       job += links[j].href + "\r\n" + links[j].description + "\r\n";
  1047.     }
  1048.     return job;
  1049.   };
  1050.   dm.performJob = function(job) {
  1051.     const file = this.createJobFile(job);
  1052.     delete job;
  1053.     Components.classes["@presenta/iGetter"]
  1054.               .getService(Components.interfaces.nsIGetterMoz)
  1055.               .NewURL(file.path);
  1056.     if(file.exists()) file.remove(0);
  1057.   };
  1058.   
  1059.   new FlashGotDM("Internet Download Accelerator");
  1060.   (new FlashGotDM("Internet Download Manager")).postSupport = true;
  1061.  
  1062.   var lg2002=new FlashGotDM("LeechGet 2002");
  1063.   var lg2004=new FlashGotDM("LeechGet");
  1064.   lg2004._bgJob=lg2002._bgJob=false;
  1065.   lg2004.super_createJob=lg2002.super_createJob=FlashGotDM.prototype.createJob;
  1066.   lg2004.createJob=lg2002.createJob=function(links, opType) {
  1067.     const service=this.service;
  1068.     var referrer;
  1069.     switch(opType) {
  1070.       case service.OP_ONE:
  1071.         return this.super_createJob(links, links.quickDownload?service.OP_ONE:service.OP_SEL);
  1072.       case service.OP_SEL:
  1073.         var htmlDoc="<html><head><title>FlashGot selection</title></head><body>";
  1074.         var l;
  1075.         for(var j=0, len=links.length; j<len; j++) {
  1076.           l=links[j];
  1077.           var des=l.description;
  1078.           var tag=l.tagName?l.tagName.toLowerCase():"";
  1079.           htmlDoc=htmlDoc.concat(tag=="img"
  1080.             ?"<img src=\""+l.href+"\" alt=\""+des
  1081.               +"\" width=\""+l.width+"\" height=\""+l.height+
  1082.               "\" />\n"
  1083.             :"<a href=\""+l.href+"\">"+des+"</a>\n");
  1084.           this.updateProgress(links,j,len);
  1085.         }
  1086.         referrer = service.httpServer.addDoc(
  1087.           htmlDoc.concat("</body></html>")
  1088.         );
  1089.         break;
  1090.        default:
  1091.         referrer=links.document.URL;
  1092.         if(referrer.match(/^\s*file:/i)) { // fix for local URLs
  1093.           // we serve local URLs through built-in HTTP server...
  1094.           return this.createJob(links,service.OP_SEL);
  1095.         }
  1096.     }
  1097.     return this.createJobHeader({ length: 0, folder: "" },opType)+referrer+"\n";
  1098.   };
  1099.  
  1100.   new FlashGotDM("Net Transport");
  1101.   new FlashGotDM("Net Transport 2");
  1102.   new FlashGotDM("NetAnts");
  1103.   new FlashGotDM("Mass Downloader");
  1104.   
  1105.   (new FlashGotDM("ReGet")).postSupport = true;
  1106.   
  1107.   const httpFtpValidator=function(url) {
  1108.     return /^(http:|ftp:)/.test(url);
  1109.   };
  1110.   dm=new FlashGotDM("Star Downloader");
  1111.   dm.cookieSupport=false;
  1112.   dm.isValidLink=httpFtpValidator;
  1113.   dm._waitForNative=false;
  1114.   
  1115.   dm=new FlashGotDM("TrueDownloader");
  1116.   dm.isValidLink=httpFtpValidator;
  1117.   dm._waitForNative=false;
  1118.   
  1119.   new FlashGotDM("Thunder");
  1120.   
  1121.   dm = new FlashGotDM("WellGet");
  1122.   dm.getRelativeExe = function() {
  1123.     try {
  1124.       return this.service.prefs.getComplexValue("WellGet.path", Components.interfaces.nsILocalFile);
  1125.     } catch(ex) {}
  1126.     return null;
  1127.   };
  1128.   dm.customSupportCheck = function() {
  1129.      try {
  1130.        var wellGetExe = this.getRelativeExe();
  1131.        var currentPath = wellGetExe.path;
  1132.        wellGetExe.initWithPath(this.service.profDir.path.substring(0,2) + dir.path.substring(2));
  1133.        if(wellGetExe.path != currentPath) {
  1134.           this.service.prefs.setComplexValue("WellGet.path", wellGetExe);
  1135.        }
  1136.        return wellGetExe.exists() && wellGetExe.isExecutable();
  1137.      } catch(ex) {
  1138.      }
  1139.      return false;
  1140.   };
  1141.   dm.createJob = function(links, opType) {
  1142.     var job = FlashGotDM.prototype.createJob.call(this, links, opType);
  1143.     var wellGetExe = this.getRelativeExe();
  1144.     if(wellGetExe) job += "\n" + wellGetExe.path;
  1145.     return job;
  1146.   };
  1147.   dm.shouldList = function() { return true; }
  1148.  
  1149.   dm=new FlashGotDMX("Aria","aria", "[-r REFERER] [-d FOLDER] -g [URL]");
  1150.   dm.createJob=function(links,opType) {
  1151.     return FlashGotDMX.prototype.createJob.call(this,links,opType) + "\nsleep 4\n" + this.unixCmd+" -s &\n";
  1152.   };
  1153.   dm._waitForNative=false;
  1154.   
  1155.   dm=new FlashGotDMX("Downloader 4 X (nt)","nt");
  1156.   dm.createJob=function(links,opType) {
  1157.     return this.unixCmd+"&\nsleep 1\n" +
  1158.       (links.folder && links.folder._fgSelected
  1159.       ? this.unixCmd+" -d '"+links.folder+"'\n"
  1160.       :"") + 
  1161.       FlashGotDMX.prototype.createJob.call(this,links,opType);
  1162.   };
  1163.   
  1164.   dm=new FlashGotDMX("Downloader 4 X","d4x","[--referer REFERER] [--directory FOLDER] [-a URL] [--al POST] [COOKIE]");
  1165.   dm.askPath=[false, true, true];
  1166.   dm.postSupport = true;
  1167.   dm.createJob = function(links, opType) {
  1168.     const service = this.service;
  1169.     const shellEsc = this.shellEsc;
  1170.     const referer = shellEsc(this.getReferrer(links));
  1171.     const folder = links.folder._fgSelected && links.folder || null;
  1172.     const quiet = service.getPref(this.codeName+".quiet",false);
  1173.     const len = links.length;
  1174.     var job;
  1175.     
  1176.     if(len>0) {
  1177.       
  1178.        var urls="";
  1179.        for(var j=0; j<len; j++) {
  1180.          urls+=" "+shellEsc(links[j].href);
  1181.          this.updateProgress(links, j, len);
  1182.        }
  1183.  
  1184.        var promptURLs_fakePost = null;
  1185.        var quietURLs_fakeCookie = null;
  1186.        
  1187.        if(quiet) {
  1188.          quietURLs_fakeCookie = urls;
  1189.          urls = null;
  1190.        } else if(len>1) {
  1191.          promptURLs_fakePost = urls;
  1192.          urls = null;
  1193.        }
  1194.        job = this.createCmdLine(
  1195.                   urls, 
  1196.                   referer,
  1197.                   quietURLs_fakeCookie || null,
  1198.                   folder,
  1199.                   promptURLs_fakePost);
  1200.     } else job = "";
  1201.     
  1202.     return job;
  1203.   };
  1204.   
  1205.   dm=new FlashGotDMX("KDE KGet","kget");
  1206.   dm.askPath=ASK_NEVER;
  1207.   
  1208.   dm=new FlashGotDMX("GNOME Gwget","gwget");
  1209.   dm.askPath=ASK_NEVER;
  1210.   
  1211.   dm=new FlashGotDMX("cURL","curl", '-L -O [--referer REFERER] [-b COOKIE] [-d POST] [URL]');
  1212.   dm.postSupport = true;
  1213.   dm.createJob=function(links,opType) {
  1214.     var job="[ -x \"`which 'xterm'`\" ] &&  CURL_CMD='xterm -e curl' || CURL_CMD='curl'\n";
  1215.     if (links.folder) job+="cd '"+links.folder+"'\n";
  1216.     this.unixCmd="$CURL_CMD";
  1217.     return job + FlashGotDMX.prototype.createJob.call(this,links,opType);
  1218.   };
  1219.  
  1220.   
  1221.   function FlashGotDMSD(version) {
  1222.     this._initMac("Speed Download "+version,"Spee");
  1223.     this.version=version;
  1224.   };
  1225.   FlashGotDMSD.prototype=new FlashGotDMMac();
  1226.   FlashGotDMSD.prototype.createJob=function(links,opType) {
  1227.     const v=this.version;
  1228.    
  1229.     var job = "tell app \""+ this.macAppName+ "\" to AddURL {";
  1230.     var urlList=[];
  1231.     var cookieList=[];
  1232.     var l;
  1233.     for(var j=0,len=links.length; j<len; j++) {
  1234.       l=links[j];
  1235.       urlList[urlList.length] = '"'+l.href+'"';
  1236.       if(v>2) {
  1237.         cookieList[cookieList.length] = '"'+this.getCookie(l,links)+'"';
  1238.       }
  1239.       this.updateProgress(links, j, len);
  1240.     }
  1241.     job+=urlList.join(',')+"}";
  1242.     if(v>2) {
  1243.       if(links.postData) { 
  1244.         job+=' with form data "'+links.postData+'"';
  1245.       }
  1246.       const referer=this.getReferrer(links);
  1247.       if(referer && referer.length) {
  1248.         job+=' from "'+referer+'"';
  1249.       }
  1250.       if(cookieList.length) {
  1251.         job+=' with cookies {' + cookieList.join(',') + '}';
  1252.       }
  1253.     }  
  1254.     return job;
  1255.   };
  1256.   
  1257.   new FlashGotDMSD(2);
  1258.   dm = new FlashGotDMSD(3);
  1259.   dm.cookieSupport = true;
  1260.   dm.postSupport = true;
  1261.   
  1262.   new FlashGotDMMac("iGetter","iGET");
  1263.   
  1264.   FlashGotDMCust.init(service);
  1265.   service.sortDMS();
  1266.   
  1267. };
  1268.  
  1269. // *****************************************************************************
  1270. // HTTP interceptor (nsIURIContentListener + http-on-modify-request observer)
  1271. // *****************************************************************************
  1272.  
  1273. function HttpInterceptor(service) {
  1274.   this.service = service;
  1275.  
  1276.   Components.classes["@mozilla.org/uriloader;1"].getService(
  1277.     Components.interfaces.nsIURILoader).registerContentListener(this);
  1278. }
  1279.  
  1280. HttpInterceptor.prototype = {
  1281.   service: null,
  1282.   
  1283.   autoStart: false,
  1284.   interceptAll: true,
  1285.   bypassAutoStart: false,
  1286.   forceAutoStart: false,
  1287.   
  1288.   lastPost: null, // last uploadChannel
  1289.   
  1290.   setup: function() { // profile initialization
  1291.     this.autoStart = this.service.getPref("autoStart", false);
  1292.     this.interceptAll = this.service.getPref("interceptAll", true);
  1293.   },
  1294.   
  1295.   dispose: function() {
  1296.     Components.classes["@mozilla.org/uriloader;1"].getService(
  1297.         Components.interfaces.nsIURILoader).unRegisterContentListener(this);
  1298.   },
  1299.   
  1300.   log: function(msg) {
  1301.     this.service.log(msg);
  1302.   },
  1303.   
  1304.   _shouldIntercept: function(contentType) {
  1305.     dump("FG: _shouldIntercept("+contentType+"\n");
  1306.     if(this.bypassAutoStart) return false;
  1307.     const service = this.service;
  1308.     if(!(service.DMS && service.DMS.found)) return false;
  1309.     if(this.forceAutoStart) return true;
  1310.     
  1311.     if(!this.autoStart) return false;
  1312.     
  1313.     if(this.interceptAll &&
  1314.       !/\bxpinstall\b/.test(contentType)) {
  1315.       return true;
  1316.     }
  1317.  
  1318.     if(contentType=="application/x-unknown-content-type") return false;
  1319.     var ms=Components.classes['@mozilla.org/uriloader/external-helper-app-service;1']
  1320.                      .getService(Components.interfaces.nsIMIMEService);
  1321.     const exts = service.extensions;
  1322.     for(var j = exts.length; j-->0;) {
  1323.       if(contentType == ms.getTypeFromExtension(exts[j])) return true;
  1324.     }
  1325.   }
  1326.   _willHandle: function(url, contentType) {
  1327.     if(!/^(http|https|ftp|sftp|rtsp|mms):/i.test(url) ) {
  1328.       if((/^\s*javascript/i).test(url)) this.log("JavaScript url intercepted: "+url);
  1329.       return false;
  1330.     }
  1331.     return true;
  1332.   }
  1333. ,
  1334.   extractPostData: function(channel) {
  1335.     if(channel instanceof Components.interfaces.nsIUploadChannel &&
  1336.        channel.uploadStream instanceof Components.interfaces.nsISeekableStream) {
  1337.       this.log("Extracting post data...");
  1338.       try {
  1339.         channel.uploadStream.seek(0,0);
  1340.         const sis=Components.classes[
  1341.           '@mozilla.org/scriptableinputstream;1'].createInstance(
  1342.           Components.interfaces.nsIScriptableInputStream);
  1343.         sis.init(channel.uploadStream);
  1344.         var postData=sis.read(sis.available()).replace(/\s$/,'').split(/[\r\n]/);
  1345.         return postData[postData.length-1];
  1346.       } catch(ex) {
  1347.         this.log(ex.message);
  1348.       } finally {
  1349.          sis.close();
  1350.       }
  1351.     }
  1352.     return null;
  1353.   },
  1354.   /* nsIURIContentListener */
  1355.   
  1356.   canHandleContent: function(contentType, isContentPreferred, desiredContentType) {
  1357.     dump("FG: canHandleContent "+contentType+"\n");
  1358.     return this._shouldIntercept(contentType);
  1359.   }
  1360. ,
  1361.   doContent: function(contentType, isContentPreferred, channel, contentHandler) {
  1362.     const ci=Components.interfaces;
  1363.     channel.QueryInterface(ci.nsIChannel);
  1364.     const uri=channel.URI;
  1365.     dump("FG: doContent "+contentType+" "+uri.spec+"\n");
  1366.     if(!this._willHandle(uri.spec,contentType)) {
  1367.       throw new Error("FlashGot not interested in "+contentType+" from "+uri.spec);
  1368.     }
  1369.     
  1370.     this.log("Intercepting download...");
  1371.  
  1372.     const pathParts=uri.path.split(/\//);
  1373.     var links=[ {
  1374.      href: channel.URI.spec, 
  1375.      title: pathParts[pathParts.length-1],
  1376.     } ];
  1377.     
  1378.     if(channel instanceof ci.nsIHttpChannel) {
  1379.       links.referrer=channel.referrer.spec;
  1380.       links.postData = this.extractPostData(channel);
  1381.     }
  1382.       
  1383.     if(this.service.download(links)) {
  1384.       this.log("...interception done!");
  1385.     } else {
  1386.        throw new Error("Can't download from this URL: "+uri.spec);
  1387.     }
  1388.     
  1389.     channel.cancel(Components.results.NS_BINDING_ABORTED); 
  1390.    
  1391.     this.log("Original request cancelled.");
  1392.     contentHandler.value=null;
  1393.     return true;
  1394.   }
  1395. ,
  1396.   isPreferred: function(contentType, desiredContentType) {
  1397.     dump("FG: isPreferred("+contentType+","+desiredContentType+"\n");
  1398.     return this._shouldIntercept(contentType);
  1399.   }
  1400. ,
  1401.   onStartURIOpen: function(uri) {
  1402.     dump("FG: onStartURIOpen "+ uri + (uri && uri.spec) + "\n");
  1403.     return false;
  1404.   }
  1405. ,
  1406.   /* http-on-modify-request Observer */
  1407.   observe: function(channel, topic, data) {
  1408.     if(channel instanceof Components.interfaces.nsIUploadChannel) {
  1409.       this.lastPost = channel.QueryInterface(Components.interfaces.nsIHttpChannel);
  1410.     }
  1411.   }
  1412. }
  1413.  
  1414.  
  1415.  
  1416.  
  1417. // *****************************************************************************
  1418. // XPCOM Service
  1419. // *****************************************************************************
  1420.  
  1421. const SHUTDOWN="profile-before-change";
  1422. const STARTUP="profile-after-change";
  1423.  
  1424. function FlashGotService() {
  1425.   
  1426.   const osvr=Components.classes['@mozilla.org/observer-service;1'].getService(
  1427.     Components.interfaces.nsIObserverService);
  1428.   
  1429.   osvr.addObserver(this,SHUTDOWN,false);
  1430.   osvr.addObserver(this,"xpcom-shutdown",false);
  1431.   osvr.addObserver(this,STARTUP,false);
  1432.   
  1433.   this.interceptor = new HttpInterceptor(this);
  1434.   osvr.addObserver(this.interceptor, "http-on-modify-request", false);
  1435.   
  1436.   
  1437. }
  1438.  
  1439. FlashGotService.prototype = {
  1440.   OP_ONE: 0, 
  1441.   OP_SEL: 1,
  1442.   OP_ALL: 2,
  1443.   OP_QET: 3
  1444. ,
  1445.   get wrappedJSObject() {
  1446.     return this;
  1447.   }
  1448. ,
  1449.   unregister: function() {
  1450.     try {
  1451.       const osvr=Components.classes['@mozilla.org/observer-service;1'].getService(
  1452.       Components.interfaces.nsIObserverService);
  1453.       osvr.removeObserver(this,SHUTDOWN);
  1454.       osvr.removeObserver(this,"xpcom-shutdown");
  1455.       osvr.removeObserver(this,STARTUP);
  1456.       osvr.removeObserver(this.interceptor, "http-on-modify-request");
  1457.       this.interceptor.dispose();
  1458.     } catch(ex) {
  1459.       this.log("Error unregistering service as observer: "+ex);
  1460.     }
  1461.   }
  1462. ,
  1463.   QueryInterface: function(iid) {
  1464.      xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_NO_INTERFACE);
  1465.      return this;
  1466.   }
  1467. ,
  1468.   /* nsIObserver */  
  1469.   observe: function(subject, topic, data) {
  1470.     if(subject == this.prefs) {
  1471.       this.syncPrefs(data);
  1472.     } else {
  1473.       switch(topic) {
  1474.         case "xpcom-shutdown":
  1475.           this.unregister();
  1476.           break;
  1477.         case SHUTDOWN: 
  1478.           this.cleanup();
  1479.           break;
  1480.         case STARTUP:
  1481.           this.initGlobals();
  1482.           this.interceptor.setup();
  1483.           break;
  1484.       }
  1485.     }
  1486.   }
  1487. ,
  1488.   syncPrefs: function(name) {
  1489.     this.logEnabled=this.getPref("logEnabled",true);
  1490.     if(name) {
  1491.       switch(name) {
  1492.         case "hide-icons":
  1493.           var w;
  1494.           for(var wins=this.windowMediator.getEnumerator(null); wins.hasMoreElements();) {
  1495.              w=wins.getNext();
  1496.              if(typeof(w.gFlashGot)=="object" && w.gFlashGot.toggleMainMenuIcon) {
  1497.                w.gFlashGot.toggleMainMenuIcon();
  1498.              }
  1499.           }
  1500.         break;
  1501.         
  1502.         case "autoStart":
  1503.         case "interceptAll":
  1504.           this.interceptor[name] = this.getPref("name");
  1505.         break;
  1506.       }
  1507.     }
  1508.   }
  1509. ,
  1510.   
  1511.   get defaultDM() {
  1512.     return this.getPref("defaultDM",null);
  1513.   }
  1514. ,
  1515.   set defaultDM(name) {
  1516.     this.setPref("defaultDM", name);
  1517.     return name;
  1518.   }
  1519. ,
  1520.   get tmpDir() {
  1521.     return this.globals.tmpDir; 
  1522.   }
  1523. ,
  1524.   get profDir() {
  1525.     return this.globals.profDir; 
  1526.   }
  1527. ,
  1528.   get DMS() {
  1529.     return this.globals.DMS;
  1530.   }
  1531. ,
  1532.   get extensions() {
  1533.     var s=this.getPref("extensions","");
  1534.     return s?s.split(','):[];
  1535.   }
  1536. ,
  1537.   set extensions(v) {
  1538.     var arr=null;
  1539.     var s = typeof(v)=="string" 
  1540.       ? v : typeof(v)=="object" && typeof(v.join)=="function" 
  1541.         ? (arr=v).join(',').replace(/[^\w\-,]/g,"") : "";
  1542.     this.setPref("extensions", s);
  1543.     return arr?arr:[];
  1544.   }
  1545. ,
  1546.   addExtension: function(ext) {
  1547.     if(ext) {
  1548.       var extensions=this.extensions;
  1549.       if(!this.extensionExists(ext,extensions)) {
  1550.         extensions[extensions.length] = ext;
  1551.         extensions.sort();
  1552.         this.extensions=extensions;
  1553.         return true;
  1554.       }
  1555.     }
  1556.     return false;
  1557.   }
  1558. ,
  1559.   removeExtension: function(ext) {
  1560.     var extensions=this.extensions;
  1561.     var j=this.indexOfExtension(ext,extensions);
  1562.     if(j>-1) {
  1563.       extensions.splice(j,1);
  1564.       this.extensions=extensions;
  1565.       return true;
  1566.     }
  1567.     return false;
  1568.   }
  1569. ,
  1570.   extensionExists: function(ext,extensions) {
  1571.     return this.indexOfExtension(ext,extensions)>-1;
  1572.   }
  1573. ,
  1574.   indexOfExtension: function(ext,extensions) {
  1575.     var ext=ext.toLowerCase();
  1576.     if(typeof(extensions)!="object") extensions=this.extensions;
  1577.     for(var j=extensions.length; j-->0;) {
  1578.       if(extensions[j].toLowerCase()==ext) return j;
  1579.     }
  1580.     return -1;
  1581.   }
  1582. ,
  1583.   _httpServer: null,
  1584.   get httpServer() {
  1585.     if(typeof(FlashGotHttpServer) != "function") {
  1586.       Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  1587.           .getService(Components.interfaces.mozIJSSubScriptLoader)
  1588.           .loadSubScript("chrome://flashgot/content/flashgotHttpServer.js", null);
  1589.     }
  1590.     return ((!this._httpServer) || this._httpServer.isDown) ?
  1591.        this._httpServer=new FlashGotHttpServer(this)
  1592.       :this._httpServer;
  1593.   }
  1594.  
  1595. ,
  1596.   download: function(links, opType, dmName) {
  1597.     if(links.length == 0) return false;
  1598.     if(!opType) opType=links.length>1?this.OP_SEL:this.OP_ONE;
  1599.     if(!dmName) dmName=this.defaultDM;
  1600.     const dm=this.DMS[dmName];
  1601.     if(!dm) {
  1602.       this.log("FlashGot error: no download manager selected!");
  1603.       return false;
  1604.     }
  1605.     
  1606.     // surrogate missing attributes
  1607.     
  1608.     if(!links.progress) {
  1609.       links.progress = { update: function() {} };
  1610.     } else {
  1611.       links.progress.update(12);
  1612.     }
  1613.     
  1614.    
  1615.     var service = this;
  1616.     this._delay(function(t) { service._downloadDelayed(links, opType, dm); }); 
  1617.     return true;
  1618.   },
  1619.   
  1620.   _downloadDelayed: function(links, opType, dm) {
  1621.     
  1622.      if(!links.postData) { 
  1623.       links.postData = "";
  1624.     } else if(!dm.postSupported) {
  1625.       // surrogate POST parameters as query string
  1626.       links[0].href += (links[0].href.indexOf("?") > -1 ?  "&" : "?") + links.postData;
  1627.     }
  1628.  
  1629.     const encodedURLs=this.getPref(dm.codeName+".encode",this.getPref("encode",true));
  1630.  
  1631.     const extFilter = this.getPref("extfilter", false) && !this.interceptor.interceptAll ?
  1632.         new RegExp("\.(" +
  1633.           this.extensions.join("|").replace(/[^\w-|]/,"") + 
  1634.           ")\\b", "i") : null;
  1635.     
  1636.     var logMsg = "Processing "+links.length+" links ";
  1637.     if(this.logEnabled && typeof(links.startTime) == "number") {
  1638.       logMsg += "scanned in ms" + (Date.now() - links.startTime);
  1639.     }
  1640.     
  1641.     
  1642.  
  1643.     var startTime = Date.now();
  1644.     const pg=links.progress;
  1645.     
  1646.     const escapeCheckNo=/(%[0-9a-f]{2,4})/i;
  1647.     const escapeCheckYes=/[\s]+/;
  1648.     
  1649.     var len = links.length;
  1650.     
  1651.     var filters = null;
  1652.     if(len > 1) {
  1653.       filters = [];
  1654.       
  1655.       const isValid = dm.isValidLink; 
  1656.       if(isValid)  filters.push(function() { return isValid(href) });
  1657.       
  1658.       if(extFilter) filters.push(function() { return extFilter.test(href) });
  1659.       
  1660.       if(filters.length) {
  1661.         filters.doFilter = function(href) {
  1662.           for(var j = this.length; j-- > 0;) if(!this[j](href)) return false;
  1663.           return true;
  1664.         }
  1665.       } else {
  1666.         filters = null;
  1667.       }
  1668.     }
  1669.  
  1670.     const map = {};
  1671.     
  1672.     var k, j, l, href, ol, pos1, pos2;
  1673.     var jCount;
  1674.     // divide et impera :)
  1675.     var chunkLen = 100;
  1676.     const chunks = Math.ceil(len / chunkLen); 
  1677.     j = 0;
  1678.     for(k = 1; k <= chunks; k++) {
  1679.       if(k == chunks) chunkLen = len % chunkLen;
  1680.       if(j > 0) {
  1681.         pg.update(10 + 30 * j / len);
  1682.       }
  1683.      
  1684.       for(jCount = j + chunkLen; j < jCount; j++) {
  1685.         l = links[j];
  1686.         l._pos = j;
  1687.         href = l.href;
  1688.         if((!filters) || filters.doFilter(href)) {
  1689.           ol = map[href];
  1690.           if(ol) { // duplicate, keep the longest description
  1691.             if(ol.description.length < l.description.length) {
  1692.               map[href]=l;
  1693.               l.href = ol.href; // keep sanitizations
  1694.             }
  1695.           } else {
  1696.             map[href] = l;
  1697.             // encoding checks
  1698.             try {
  1699.               if(encodedURLs) { 
  1700.                 if(escapeCheckYes.test(href) || !escapeCheckNo.test(href)) { 
  1701.                   href=encodeURI(href);
  1702.                 }
  1703.                 // workaround for malformed hash urls... I have still to think about it :-)
  1704.                 while((pos1=href.indexOf('#'))>-1 
  1705.                   && ((pos2=href.indexOf('?'))<0 
  1706.                       || pos2>pos1 || pos1!=href.lastIndexOf('#'))) {
  1707.                   href=href.substring(0,pos1)+'%23'+href.substring(pos1+1);
  1708.                 }
  1709.                 l.href=href;
  1710.               } else {  
  1711.                 l.href=decodeURI(href);
  1712.               }
  1713.             } catch(e) {
  1714.               dump("Problem "
  1715.                 + ( encodedURLs ? "escaping" : "unescaping")
  1716.                 + " URL " + href + ": "+ e.message + "\n");
  1717.             }
  1718.           }
  1719.         }
  1720.       }
  1721.     }
  1722.    
  1723.     links.length = 0;
  1724.     for(href in map) links[links.length] = map[href];
  1725.     
  1726.     if(links.length>1) {
  1727.       dm.log("Sorting again "+links.length+" links");
  1728.       links.sort(function(a,b) {
  1729.         a=a._pos; b=b._pos;
  1730.         return a>b?1:a<b?-1:0;
  1731.       });
  1732.     }
  1733.     pg.update(50);
  1734.     
  1735.     dm.log("Preprocessing done in ms" + (Date.now() - startTime) );
  1736.     
  1737.     // "true" download
  1738.     this._delay(function(t) {
  1739.         dm.log("Starting dispatch");
  1740.         var startTime = Date.now();
  1741.     
  1742.         dm.download(links, opType);
  1743.  
  1744.         var now = Date.now();
  1745.         var logMsg = "Dispatch done in ms" + (now - startTime);
  1746.         if(typeof(links.startTime) == "number") { 
  1747.           logMsg += "\nTotal processing time: ms" + (now - links.startTime);
  1748.         }  
  1749.         dm.log(logMsg);
  1750.       });
  1751.   }
  1752. ,
  1753.   _delay: function(callback, time) {
  1754.      var timerCallback = { notify: callback }; 
  1755.      Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer)
  1756.               .initWithCallback(timerCallback, time || 0, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
  1757.   }
  1758. ,
  1759.   yeld: function() {
  1760.     const eqs = Components.interfaces.nsIEventQueueService;
  1761.     Components.classes["@mozilla.org/event-queue-service;1"]
  1762.       .getService(eqs).getSpecialEventQueue(eqs.UI_THREAD_EVENT_QUEUE)
  1763.       .processPendingEvents();
  1764.   },
  1765.   
  1766.   get bgProcessing() {
  1767.     return false;
  1768.       // this.getPref("bgProcessing", true);
  1769.   }
  1770. ,
  1771.   get prefService() {
  1772.     return Components.classes["@mozilla.org/preferences-service;1"].getService(
  1773.       Components.interfaces.nsIPrefService);
  1774.   }
  1775. ,
  1776.   savePrefs: function() {
  1777.     return this.prefService.savePrefFile(null);
  1778.   }
  1779. ,
  1780.   getPref: function(name,def) {
  1781.     const IPC=Components.interfaces.nsIPrefBranch;
  1782.     const prefs=this.prefs;
  1783.     try {
  1784.       switch(prefs.getPrefType(name)) {
  1785.         case IPC.PREF_STRING:
  1786.           return prefs.getCharPref(name);
  1787.         case IPC.PREF_INT:
  1788.           return prefs.getIntPref(name);
  1789.         case IPC.PREF_BOOL:
  1790.           return prefs.getBoolPref(name);
  1791.       }
  1792.     } catch(e) {}
  1793.     return def;
  1794.   }
  1795. ,
  1796.   setPref: function(name,value) {
  1797.     const prefs=this.prefs;
  1798.     switch(typeof(value)) {
  1799.       case "string":
  1800.           prefs.setCharPref(name,value);
  1801.           break;
  1802.       case "boolean":
  1803.         prefs.setBoolPref(name,value);
  1804.         break;
  1805.       case "number":
  1806.         prefs.setIntPref(name,value);
  1807.         break;
  1808.       default:
  1809.         throw new Error("Unsupported type "+typeof(value)+" for preference "+name);
  1810.     }
  1811.   }
  1812. ,
  1813.   _bundle: null,
  1814.   get bundle() {
  1815.     if(!this._bundle) {
  1816.       function getBundle(url) {
  1817.         try {
  1818.           var bundle = bs.createBundle(url);
  1819.           bundle.GetStringFromName("flashgot");
  1820.         } catch(ex) {
  1821.           dump("\n"+ex+"\n");
  1822.           bundle = null;
  1823.         }
  1824.         return bundle;
  1825.       }
  1826.       
  1827.       var bs = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(
  1828.         Components.interfaces.nsIStringBundleService);
  1829.       if(! ( 
  1830.             (this._bundle=getBundle("chrome://flashgot/locale/flashgot.properties") )
  1831.             || (this._bundle=bs.createBundle("chrome://flashgot/content/en-US/flashgot.properties"))
  1832.             )
  1833.         ) {
  1834.         this._bundle = {
  1835.           formatStringFromName: function(name,parms) {
  1836.             return name+" ["+parms.join(',')+"]"
  1837.           },
  1838.           GetStringFromName: function(name) {
  1839.             return name;
  1840.           }
  1841.         };
  1842.       }
  1843.     }
  1844.     return this._bundle;
  1845.   }
  1846. ,
  1847.   getString: function(name,parms) {
  1848.     const bundle=this.bundle;
  1849.     try {
  1850.       return (parms
  1851.           ?bundle.formatStringFromName(name,parms,parms.length)
  1852.           :bundle.GetStringFromName(name));
  1853.     } catch(ex) {
  1854.       return "???";
  1855.     }
  1856.   }
  1857. ,
  1858.   _logFile: null,
  1859.   get logFile() {
  1860.     if(this._logFile==null) {
  1861.       this._logFile=this.profDir.clone();
  1862.       this._logFile.append("flashgot.log");
  1863.     }
  1864.     return this._logFile;
  1865.   }
  1866. ,
  1867.   logStream: null,
  1868.   logEnabled: false,
  1869.   log: function(msg) {
  1870.     if(this.logEnabled) {
  1871.       try {
  1872.         if(!this.logStream) {
  1873.           const logFile=this.logFile;
  1874.           const logStream=Components.classes["@mozilla.org/network/file-output-stream;1"
  1875.             ].createInstance(Components.interfaces.nsIFileOutputStream );
  1876.           logStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0 );
  1877.           this.logStream=logStream;
  1878.           const header="*** Log start at "+new Date().toGMTString()+"\n";
  1879.           this.logStream.write(header,header.length);
  1880.         }
  1881.         
  1882.         if(msg!=null) {
  1883.           msg+="\n";
  1884.           this.logStream.write(msg,msg.length);
  1885.         }
  1886.         this.logStream.flush();
  1887.       } catch(ex) {
  1888.         dump(ex.message+"\noccurred logging this message:\n"+msg);
  1889.       }
  1890.     }
  1891.   }
  1892. ,
  1893.   dumpStack: function(msg) {
  1894.     dump( (msg?msg:"")+"\n"+new Error().stack+"\n");
  1895.   }
  1896. ,
  1897.   clearLog: function() {
  1898.     try {
  1899.       if(this.logStream) {
  1900.         try {
  1901.           this.logStream.close();
  1902.         } catch(eexx) {
  1903.           dump(eexx.message);
  1904.         }
  1905.       }
  1906.       if(this.logFile) this.logFile.remove(true);
  1907.       this.logStream=null;
  1908.       this.log(null);
  1909.     } catch(ex) { dump(ex.message); }
  1910.   } 
  1911. ,
  1912.   get windowMediator() {
  1913.     return Components.classes["@mozilla.org/appshell/window-mediator;1"
  1914.       ].getService(Components.interfaces.nsIWindowMediator);
  1915.   }
  1916. ,
  1917.   getWindow: function() {
  1918.     return this.windowMediator.getMostRecentWindow(null);
  1919.   }
  1920. ,
  1921.   _globals: null,
  1922.   get globals() {
  1923.     if(!this._initialized) {
  1924.       this.initGlobals();
  1925.     }
  1926.     return this._globals;
  1927.   }
  1928. ,
  1929.   PREFS_BRANCH: "flashgot."
  1930. ,
  1931.   _prefs: null,
  1932.   get prefs() {
  1933.     var prefs=this._prefs;
  1934.     if(!prefs) {
  1935.       this._prefs=prefs=this.prefService.getBranch(this.PREFS_BRANCH
  1936.         ).QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  1937.     }
  1938.     return prefs;
  1939.   }
  1940. ,
  1941.   _initialized: false,
  1942.   initGlobals: function() {
  1943.     if(this._globals || this._initialized) return;
  1944.     
  1945.     function prepareTmp(t) {
  1946.       t.append("flashgot."+encodeURI(profDir.leafName).replace(/%/g,"_"));
  1947.       if(t.exists()) {
  1948.        if(!t.isDirectory()) t.createUnique(1,0700);
  1949.       } else {
  1950.         t.create(1,0700);
  1951.       }
  1952.       return t;
  1953.     }
  1954.     
  1955.     try {
  1956.       const startTime = Date.now();
  1957.       const prefs=this.prefs;
  1958.       const cc=Components.classes;
  1959.       const ci=Components.interfaces; 
  1960.  
  1961.       const fileLocator=cc["@mozilla.org/file/directory_service;1"].getService(
  1962.         ci.nsIProperties);
  1963.       const profDir=fileLocator.get("ProfD",ci.nsIFile);
  1964.      
  1965.       var tmpDir;
  1966.       try {
  1967.         tmpDir=prepareTmp(prefs.getComplexValue("tmpDir", ci.nsILocalFile));
  1968.       } catch(ex) {
  1969.         tmpDir=prepareTmp(fileLocator.get("TmpD", ci.nsILocalFile));
  1970.       }
  1971.        
  1972.       this._globals={
  1973.         tmpDir: tmpDir,
  1974.         profDir: profDir,
  1975.         prefs: prefs
  1976.       };
  1977.       
  1978.       prefs.addObserver("", this, false);
  1979.       this.syncPrefs();
  1980.       
  1981.       this.log("Per-session init started");
  1982.         
  1983.       this._setupLegacyPrefs();
  1984.  
  1985.       this._globals.DMS=this.checkDownloadManagers(true, false);
  1986.       this.log("Per-session init done in " + (Date.now() - startTime) + "ms");
  1987.     } catch(initEx) {
  1988.       this._initException=initEx;
  1989.     }
  1990.     this._initialized=true; 
  1991.   }
  1992. ,
  1993.   dispose: function() {
  1994.     this.prefs.removeObserver("",this);
  1995.     this._prefs=null;
  1996.     this._initialized=false;
  1997.     this._globals=null;
  1998.   }
  1999. ,
  2000.   createCustomDM: function(name) {
  2001.     const dm=new FlashGotDMCust(name);
  2002.     if(name && name.length) {
  2003.       FlashGotDMCust.persist(this);
  2004.       this.sortDMS();
  2005.       this.checkDownloadManagers(false,false);
  2006.     }
  2007.     return dm;
  2008.   }
  2009. ,
  2010.  removeCustomDM: function(name) {
  2011.    const dms=FlashGotDM.dms;
  2012.    for(var j=dms.length; j-->0;) {
  2013.      if(dms[j].custom && dms[j].name==name) {
  2014.        dms.splice(j,1);
  2015.        delete dms[name];
  2016.      }
  2017.    }
  2018.    FlashGotDMCust.persist(this);
  2019.    this.checkDownloadManagers(false,false);
  2020.  }
  2021. ,
  2022.   sortDMS: function() {
  2023.     FlashGotDM.dms.sort(function(a,b) { a=a.name.toLowerCase(); b=b.name.toLowerCase(); return a>b?1:a<b?-1:0; });
  2024.   }
  2025.   checkDownloadManagers: function(init,detect) {
  2026.     
  2027.     if(init || detect) {
  2028.       FlashGotDM.init(this);
  2029.     }
  2030.     
  2031.     const dms=FlashGotDM.dms;
  2032.     dms.found=false;
  2033.     var defaultDM=this.defaultDM;
  2034.     if(!dms[defaultDM]) defaultDM=null;
  2035.     
  2036.     detect=detect || this.getPref("detect.auto",true);
  2037.  
  2038.     var j,dm;
  2039.     var cache;
  2040.     
  2041.     if(!detect) {
  2042.       cache=this.getPref("detect.cache","").split(",");
  2043.       for(j=dms.length; j-->0;) {
  2044.         dm=dms[j]._supported = false;
  2045.       }
  2046.       var name;
  2047.       for(j=cache.length; j-->0;) {
  2048.         name=cache[j];
  2049.         if(name.length && typeof(dm=dms[name])=="object" && dm.name==name) {
  2050.           dm._supported = true;
  2051.         }
  2052.       }
  2053.     }
  2054.     
  2055.     cache=[];
  2056.     var exclusive;
  2057.     var firstSupported=null;
  2058.     for(j=dms.length; j-- >0;) {
  2059.       dm=dms[j];
  2060.       if(dm.supported) {
  2061.         dms.found=true;
  2062.         cache[cache.length] = firstSupported = dm.name;
  2063.         if(dm.exclusive) exclusive=true;
  2064.       } else {
  2065.         this.log("Warning: download manager "+dm.name+" not found");
  2066.         if(defaultDM==dm.name) {
  2067.           defaultDM=null;
  2068.           this.log(dm.name+" was default download manager: resetting.");
  2069.         }
  2070.       }
  2071.     }
  2072.     
  2073.     this.setPref("detect.cache",cache.join(","));
  2074.     
  2075.     if( (!defaultDM) && firstSupported!=null) {
  2076.       this.defaultDM=firstSupported;
  2077.       this.log("Default download manager set to "+this.defaultDM);
  2078.     } else if(!dms.found) {
  2079.       this.log("Serious warning! no supported download manager found...");
  2080.     } 
  2081.     if(exclusive) {
  2082.       for(j=dms.length; j-->0;) {
  2083.         if(! (dms[j].custom || dms[j].exclusive) ) {
  2084.           dms.splice(j,1);
  2085.         }
  2086.       }
  2087.     }
  2088.     
  2089.     return dms;
  2090.   }
  2091. ,
  2092.   _referrerSpoofer: null,
  2093.   get referrerSpoofer() {
  2094.     if(typeof(ReferrerSpoofer) != "function") {
  2095.       Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  2096.           .getService(Components.interfaces.mozIJSSubScriptLoader)
  2097.           .loadSubScript("chrome://flashgot/content/referrerSpoofer.js", null);
  2098.     }
  2099.     return (!this._httpServer) ? this._referrerSpoofer = new ReferrerSpoofer() :this._referrerSpoofer;
  2100.   }
  2101. ,
  2102.   _cleaningup: false
  2103. ,
  2104.   cleanup: function() {
  2105.     if(this._cleaningup) return;
  2106.     try {
  2107.       this._cleaningup=true;
  2108.       this.log("Starting cleanup");
  2109.       if(this._httpServer) {
  2110.         this._httpServer.shutdown();
  2111.       }
  2112.       
  2113.       try {
  2114.         FlashGotDM.cleanup();
  2115.       } catch(eexx) {
  2116.         dump(eexx.message);
  2117.       }
  2118.       
  2119.       if(this._globals && this._globals.tmpDir.exists()) {
  2120.         try {
  2121.           this._globals.tmpDir.remove(true);
  2122.         } catch(eexx) {
  2123.           this.log("Can't remove "+this._globals.tmpDir.path+", maybe still in use: "+eexx);
  2124.         }
  2125.       }
  2126.       this._bundle=null;
  2127.       this.log("Cleanup done");
  2128.       if(this._logFile) try {
  2129.         if(this.logStream) this.logStream.close();
  2130.         var maxLogSize=Math.max(Math.min(this.getPref('maxLogSize',100000),1000000),50000);
  2131.         const logFile=this.logFile;
  2132.         const logSize=logFile.fileSize;
  2133.         if(logSize>maxLogSize) { // log rotation
  2134.           dump("Cutting log (size: "+logSize+", max: "+maxLogSize+")");
  2135.           const cc=Components.classes;
  2136.           const ci=Components.interfaces;
  2137.          
  2138.           const logBak=logFile.clone();
  2139.           logBak.leafName=logBak.leafName+".bak";
  2140.           if(logBak.exists()) logBak.remove(true);
  2141.           logFile.copyTo(logBak.parent,logBak.leafName);
  2142.           const is=cc['@mozilla.org/network/file-input-stream;1'].createInstance(
  2143.             ci.nsIFileInputStream);
  2144.           is.init(logBak,0x01, 0400, null);
  2145.           is.QueryInterface(ci.nsISeekableStream);
  2146.           is.seek(ci.nsISeekableStream.NS_SEEK_END,-maxLogSize);
  2147.           const sis=cc['@mozilla.org/scriptableinputstream;1'].createInstance(
  2148.           ci.nsIScriptableInputStream);
  2149.           sis.init(is);
  2150.           var buffer;
  2151.           var content="\n";
  2152.           var logStart=-1;
  2153.           while(buffer=sis.read(5000)) {
  2154.             content+=buffer;
  2155.             if((logStart=content.indexOf("\n*** Log start at "))>-1) { 
  2156.               content=content.substring(logStart);
  2157.               break;
  2158.             }
  2159.             content=buffer;
  2160.           }
  2161.           if(logStart>-1) {
  2162.              const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  2163.               ci.nsIFileOutputStream);
  2164.             os.init(logFile,0x02 | 0x08 | 0x20, 0700, 0);
  2165.             os.write(content,content.length);
  2166.             while(buffer=sis.read(20000)) {
  2167.               os.write(buffer,buffer.length);
  2168.             } 
  2169.             os.close();
  2170.           }
  2171.           sis.close();
  2172.         }
  2173.       } catch(eexx) {
  2174.         dump("Error cleaning up log: "+eexx);
  2175.       }
  2176.       this.logStream=null;
  2177.     } catch(ex) {
  2178.        this.log(ex);
  2179.     }
  2180.     this._cleaningup=false;
  2181.     this.dispose();
  2182.   }
  2183. ,
  2184.   readFile: function(file) {
  2185.     const cc=Components.classes;
  2186.     const ci=Components.interfaces; 
  2187.     
  2188.     const is = cc["@mozilla.org/network/file-input-stream;1"].createInstance(
  2189.           ci.nsIFileInputStream );
  2190.     is.init(file ,0x01, 0400, null);
  2191.     const sis = cc["@mozilla.org/scriptableinputstream;1"].createInstance(
  2192.       ci.nsIScriptableInputStream );
  2193.     sis.init(is);
  2194.     const res=sis.read(sis.available());
  2195.     is.close();
  2196.     return res;
  2197.   }
  2198. ,
  2199.   writeFile: function(file, content, charset) {
  2200.     const cc=Components.classes;
  2201.     const ci=Components.interfaces;
  2202.     const unicodeConverter = cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(
  2203.     ci.nsIScriptableUnicodeConverter);
  2204.     try {
  2205.       unicodeConverter.charset = charset?charset:"UTF-8";
  2206.     } catch(ex) {
  2207.       unicodeConverter.charset = "UTF-8";
  2208.     }
  2209.     content=unicodeConverter.ConvertFromUnicode(content);
  2210.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  2211.       ci.nsIFileOutputStream);
  2212.     os.init(file,0x02,0700,0);
  2213.     os.write(content,content.length);
  2214.     os.close();
  2215.   }
  2216. ,
  2217.   _lookupMethod: null,
  2218.   get lookupMethod() {
  2219.     return this._lookupMethod?this._lookupMethod:(this._lookupMethod = 
  2220.       (Components.utils && Components.utils.lookupMethod)
  2221.         ?Components.utils.lookupMethod:Components.lookupMethod);
  2222.   }
  2223. ,
  2224.   _setupLegacyPrefs: function() {
  2225.     try {
  2226.       const file=this._globals.profDir.clone();
  2227.       const defFile=file.clone();
  2228.       file.append("pref");
  2229.       file.append("flashgot.js");
  2230.       defFile.append("prefs.js");
  2231.       if(file.exists() && defFile.exists()) {
  2232.         this.prefService.readUserPrefs(file);
  2233.         this.prefService.readUserPrefs(defFile);
  2234.         this.savePrefs();
  2235.         file.remove(true);
  2236.       }
  2237.     } catch(e) {
  2238.       this.log(e.message);
  2239.     }
  2240.   }
  2241. ,
  2242.   showDMSReference: function() {
  2243.     this.getWindow().open("http://www.flashgot.net/dms","_blank");
  2244.   }
  2245.   dirtyJobsDone: false
  2246. }
  2247.  
  2248. // XPCOM Scaffolding code
  2249.  
  2250. // component defined in this file
  2251.  
  2252. const SERVICE_NAME="FlashGot Service";
  2253. const SERVICE_CID =
  2254.     Components.ID("{2a55fc5c-7b31-4ee1-ab15-5ee2eb428cbe}");
  2255. const SERVICE_CTRID =
  2256.     "@maone.net/flashgot-service;1";
  2257.     
  2258. const SERVICE_CONSTRUCTOR=FlashGotService;
  2259. const SERVICE_FLAGS = 3; // SINGLETON | THREADSAFE
  2260.  
  2261. // interfaces implemented by this component
  2262. const SERVICE_IIDS = 
  2263. Components.interfaces.nsISupports,
  2264. Components.interfaces.nsISupportsWeakReference,
  2265. Components.interfaces.nsIClassInfo,
  2266. Components.interfaces.nsIObserver,
  2267. Components.interfaces.nsIURIContentListener
  2268. ];
  2269.  
  2270. // Factory object
  2271. const SERVICE_FACTORY = {
  2272.   _instance: null,
  2273.   createInstance: function (outer, iid) {
  2274.     if (outer != null)
  2275.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  2276.  
  2277.     xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_INVALID_ARG);
  2278.     // kept this for flexibility sake, but we're really adopting an
  2279.     // early instantiation and late init singleton pattern
  2280.     return this._instance==null?this._instance=this._create():this._instance;
  2281.   },
  2282.   _create: function() {
  2283.     var obj=new SERVICE_CONSTRUCTOR();
  2284.     obj.__defineGetter__("classDescription",function() { return SERVICE_NAME; });
  2285.     obj.__defineGetter__("classID",function() { return SERVICE_CID; });
  2286.     obj.__defineGetter__("classIDNoAlloc",function() { return SERVICE_CTRID; });
  2287.     obj.__defineGetter__("contractID",function() { return SERVICE_CTRID; });
  2288.     obj.__defineGetter__("flags",function() { return SERVICE_FLAGS; });
  2289.     obj.__defineGetter__("implementationLanguage",function() { return 2; });
  2290.     obj.getHelperForLanguage = function() { return null; };
  2291.     obj.getInterfaces = function(count) { 
  2292.       count.value = 0; 
  2293.       return null; 
  2294.     };
  2295.     return obj;
  2296.   }
  2297. };
  2298.  
  2299. function xpcom_checkInterfaces(iid,iids,ex) {
  2300.   for(var j=iids.length; j-- >0;) {
  2301.     if(iid.equals(iids[j])) return true;
  2302.   }
  2303.   throw ex;
  2304. }
  2305.  
  2306. // Module
  2307.  
  2308. var Module = new Object();
  2309. Module.firstTime=true;
  2310. Module.registerSelf = function (compMgr, fileSpec, location, type) {
  2311.   if(this.firstTime) {
  2312.    
  2313.     debug("*** Registering "+SERVICE_CTRID+".\n");
  2314.     
  2315.     compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar
  2316.       ).registerFactoryLocation(SERVICE_CID,
  2317.       SERVICE_NAME,
  2318.       SERVICE_CTRID, 
  2319.       fileSpec,
  2320.       location, 
  2321.       type);
  2322.       
  2323.     Components.classes['@mozilla.org/categorymanager;1'].getService(
  2324.       Components.interfaces.nsICategoryManager
  2325.      ).addCategoryEntry("app-startup",
  2326.         SERVICE_NAME, "service," + SERVICE_CTRID, true, true, null);
  2327.       
  2328.     this.firstTime=false;
  2329.   } 
  2330. }
  2331. Module.unregisterSelf = function(compMgr, fileSpec, location) {
  2332.   compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar
  2333.     ).unregisterFactoryLocation(SERVICE_CID, fileSpec);
  2334.   Components.classes['@mozilla.org/categorymanager;1'].getService(
  2335.       Components.interfaces.nsICategoryManager
  2336.      ).deleteCategoryEntry("app-startup",SERVICE_NAME, true);
  2337. }
  2338.  
  2339. Module.getClassObject = function (compMgr, cid, iid) {
  2340.   if(cid.equals(SERVICE_CID))
  2341.     return SERVICE_FACTORY;
  2342.  
  2343.   if (!iid.equals(Components.interfaces.nsIFactory))
  2344.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  2345.   
  2346.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2347.     
  2348. }
  2349.  
  2350. Module.canUnload = function(compMgr) {
  2351.   return true;
  2352. }
  2353.  
  2354. // entrypoint
  2355. function NSGetModule(compMgr, fileSpec) {
  2356.   return Module;
  2357. }
  2358.  
  2359.  
  2360.  
  2361.